with win32api_interface.memory.helper; use win32api_interface.memory.helper;

with ffxilog_interface_patterns;

package body ffxilog_interface is
  function Get_Pointer_To_Log_Info(Access_Handle         : in Handle;
                                   FFXIMain_base_address : in Address)
                                   return Log_Info_Type is
    RootAddress : Address;
    FirstDereference : Address;
    OffsetFromFirstDereference : Address;
    SecondDereference : Address := System.Null_Address;
  begin
    RootAddress := FFXIMain_base_address + FFXiMainStaticOffset;
    FirstDereference := Follow_Pointer(Access_Handle, RootAddress);
    OffsetFromFirstDereference := FirstDereference + 4;
    SecondDereference := Follow_Pointer(Access_Handle, OffsetFromFirstDereference);
    return Log_Info_Type(SecondDereference);
  end Get_Pointer_To_Log_Info;

  function Get_Pointer_To_Current_Log(Access_Handle    : in Handle;
                                      Log_Info_Address : in Address)
                                      return Address is
  begin
    return Follow_Pointer(Access_Handle,
                          Log_Info_Address+Offset_To_Current_Log_Ptr);
  end Get_Pointer_To_Current_Log;

  function Get_Pointer_To_Archived_Log(Access_Handle    : in Handle;
                                       Log_Info_Address : in Address)
                                       return Address is
  begin
    return
      Follow_Pointer(Access_Handle, Log_Info_Address+Offset_To_Archived_Log_Ptr)
      + Last_Size;
  end Get_Pointer_To_Archived_Log;

  function Get_Number_of_Lines(Access_Handle    : in Handle;
                               Log_Info_Address : in Log_Info_Type)
                               return Integer is
    function Attempt return Integer is
    begin
      Return Read_Int1byte(Access_Handle,
                           Address(Log_Info_Address)+Offset_To_Line_Number);
    end Attempt;
  begin
    return Attempt;
  exception
    when Read_Bytes_Error =>
      delay 0.1;
      return Attempt;
  end get_number_of_lines;

  function Get_Log_Size(Access_Handle    : in Handle;
                        Log_Info_Address : in Address) return integer is
    n : integer;
    function Attempt return Integer is
    begin
      n := Read_Int2bytes(Access_Handle, Log_Info_Address+Offset_To_Log_Size);
      return n;
    end Attempt;
  begin
    return Attempt;
  exception
    when Read_Bytes_Error =>
      delay 0.1;
      return Attempt;
  end Get_Log_Size;

  function get_line(Current_Log : in String;
                    Line        : in Integer) return Log_Line is
    n, i, j : integer;
    header_size : constant integer := 57;
    Error_str : constant string := "Log Access Error";

    procedure skip_lines is
    begin
    n := 0; j := 1;
      while n < line loop
        while Current_Log(j) /= character'val(0) and j < Current_Log'length
        loop
          j := j+1;
        end loop;
        if Current_Log(j) = character'val(0) then n := n+1; end if;
        j := j+1;
      end loop;
    end skip_lines;

    procedure get_line_text_start is
    begin
      i := 1;
      while Current_Log (j-1-i) /= Character'val(0)
        and j-1-i > current_Log'first
      loop
        i:= i+1;
      end loop;
      if Current_Log (j-1-i) = Character'val(0) then
        i := i-1;
      end if;
      i := i-header_size+1;
    end get_line_text_start;
  begin
    skip_lines;
    get_line_text_start;
    declare
      Line : Log_Line(Current_Log(j-i..j-1-1)'length);
    begin
      Line.Text := Current_Log(j-i..j-1-1);
      if Current_Log(j-i-header_size..j-i-header_size+1) =
        ffxilog_interface_patterns.Area then
        Line.whatis := Area;
      elsif Current_Log(j-i-header_size..j-i-header_size+1) =
        ffxilog_interface_patterns.iTell then
        Line.whatis := iTell;
      elsif Current_Log(j-i-header_size..j-i-header_size+1) =
        ffxilog_interface_patterns.oTell then
        Line.whatis := oTell;
      elsif Current_Log(j-i-header_size..j-i-header_size+1) =
        ffxilog_interface_patterns.iLinkshell then
        Line.whatis := iLinkshell;
      elsif Current_Log(j-i-header_size..j-i-header_size+1) =
        ffxilog_interface_patterns.oLinkshell then
        Line.whatis := oLinkshell;
      elsif Current_Log(j-i-header_size..j-i-header_size+1) =
        ffxilog_interface_patterns.iParty then
        Line.whatis := iParty;
      elsif Current_Log(j-i-header_size..j-i-header_size+1) =
        ffxilog_interface_patterns.oParty then
        Line.whatis := oParty;
      elsif Current_Log(j-i-header_size..j-i-header_size+1) =
        ffxilog_interface_patterns.iSay then
        Line.whatis := iSay;
      elsif Current_Log(j-i-header_size..j-i-header_size+1) =
        ffxilog_interface_patterns.oSay then
        Line.whatis := oSay;
      elsif Current_Log(j-i-header_size..j-i-header_size+1) =
        ffxilog_interface_patterns.iShout then
        Line.whatis := iShout;
      elsif Current_Log(j-i-header_size..j-i-header_size+1) =
        ffxilog_interface_patterns.oShout then
        Line.whatis := oShout;
      else
        Line.whatis := other;
      end if;
      return Line;
    end;
  exception
    when constraint_error =>
      return (Error_str'length, Program, Error_str);
  end get_line;

  function Get_Log(Access_Handle    : in Handle;
                   Log_Info_Address : in Log_Info_Type) return string is
    function Attempt return string is
      Log_addr : Address :=
        Get_Pointer_To_Current_Log(Access_Handle, Address(Log_Info_Address));
      Size     : Integer :=
        Get_Log_Size(Access_Handle, Address(Log_Info_Address));
      Log      : string (1..Size);
    begin
      Log := Read_Bytes(Access_Handle, Log_addr, Size);
      Last_Size := Storage_Offset(Size);
      return Log;
    end Attempt;
  begin
    Return Attempt;
  exception
    when Read_Bytes_Error =>
      delay 0.1;
      return Attempt;
  end Get_Log;

  function Get_Archived_Log(Access_Handle    : in Handle;
                            Log_Info_Address : in Log_Info_Type;
                            last_valid_line  : in Integer) return string is
    function Get_Log_End(Log : in string) return integer is
      n, j : integer;
    begin
      n := 0; j := 1;
      while n < 50-last_valid_line loop
        while Log(j) /= character'val(0) and j < Log'length loop
          j := j+1;
        end loop;
        if Log(j) = character'val(0) then n := n+1; end if;
        j := j+1;
      end loop;
      return j-1;
    end Get_Log_End;

    function Attempt return string is
      Archived_Log_Ptr : Address
        := Get_Pointer_To_Archived_Log(Access_Handle, Address(Log_Info_Address));
      Archived_Log : constant String :=
        Read_Bytes(Access_Handle, Archived_Log_Ptr, 300*(50-last_valid_line));
    begin
      return Archived_Log(1..Get_Log_End(Archived_Log));
    end Attempt;
  begin
    return Attempt;
  exception
    when Read_Bytes_Error =>
      delay 0.1;
      return Attempt;
  end Get_Archived_Log;
end ffxilog_interface;
